<html>
<head>
  <title>Evernote Export</title>
  <basefont face="微软雅黑" size="2" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta name="exporter-version" content="YXBJ Windows/603139 (zh-CN, DDL); Windows/10.0.0 (Win64); EDAMVersion=V2;"/>
  <style>
    body, td {
      font-family: 微软雅黑;
      font-size: 10pt;
    }
  </style>
</head>
<body>
<a name="596"/>

<div>
<span><div><div><div>java跨平台的意义在于一次编译 处处执行 这与jvm的功劳密不可分  有了jvm这个抽象层后 java就可以实现跨平台</div><div>jdk包括jre和一些工具，jre包括jvm和一些类库</div><div>jdk8的元空间(元数据区)就相当于jdk8之前的方法区  元空间在直接内存上，不在与堆连续</div><div>本地方法栈：调用本地的一些方法库</div><div>程序计数器：告诉我们每一个线程执行在哪一个位置、</div><div>直接内存：电脑上内存</div><div><br/></div><div>年轻代：主要存放新创建的对象 内存大小较小 垃圾回收比较频繁</div><div>年老代：主要存放jvm认为周期较长的对象：就是经过几次的年轻代的垃圾回收后任然存在的 内存大小相对较大，垃圾回收相对没有那么频繁</div><div>jdk8及其以后取消了永久代</div><div><br/></div><div><br/></div><div>Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的，即生命周期和线程相同。Java虚拟机栈和线程同时创建，用于存储栈帧。每个方法在执行时都会创建一个栈帧(Stack Frame)，用于存储局部变量表、操作数栈、动态 链接、方法出口等信息。每一个方法从调用直到执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过 程。</div><div><br/></div><div>程序计数器：也叫pc寄存器是一块较小的内存空间：它可以看作是当前线程所执行的字节码的行号指示器</div><div>看到反编译字节码文件的两种方式：</div><ol><li><div>找到字节码文件右键打开终端 输入 javap -v 类名  的指令</div></li><li><div>使用插件</div></li></ol><div><br/></div><div>这是设置占内存大小的</div><div><img src="jvm_files/Image.png" type="image/png" data-filename="Image.png"/></div><div><br/></div><div><br/></div><div>下图是局部变量表里存放的一些值</div><div>右上图中的start pc 中的0表示pc寄存器的指令地址0  line number表示代码中的第五行 以此类推</div><div>右下表中的name 中args 表示的是main方法中的参数 args x表示变量x 依此类推</div><div><br/></div><div><br/></div><div><img src="jvm_files/Image [1].png" type="image/png" data-filename="Image.png"/></div><div>操作数栈：是一个先入后出栈</div><div>如下图演示执行过程：</div><ol><li><div>当程序第一次运行时，指针找到第“0”个地址，此时被程序计数器拿到</div></li><li><div>此时执行对应的第一个命令，”1“定义到操作数栈中</div></li><li><div>偏移量继续向下走此时pc指令地址：1 赋值给程序计数器 </div></li><li><div>然后进行第二个命令：保存 pc指令地址1：就是将操作数栈中的1 放到局部变量中</div></li><li><div>按上述过程执行：加载，返回结果</div></li></ol><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><img src="jvm_files/Image [2].png" type="image/png" data-filename="Image.png"/></div><div><br/></div><div>动态链接</div><div>作用：将符号引用转换成直接引用</div><div><br/></div><div><img src="jvm_files/Image [3].png" type="image/png" data-filename="Image.png"/></div><div><br/></div><div>堆：java虚拟机管理的内存中最大的一块</div><div>堆在虚拟机启动的时候创建</div><div>堆基本上存放了绝大部分对象</div><div>堆是所有线程共享的</div><div>堆中也包含私有的线程缓冲区：TLAB-&gt; Thread Local Allocation Buffer   它属于一个私有区域</div><div>唯一目的是存放对象实例， 几乎所有的对象实例以及数组都要在这里分配内存。</div><div>是垃圾收集器管理的主要区域，<span style="font-size: unset; color: unset; font-family: unset;">所以时候堆也被称为GC堆，</span> 从内存回收的角度来看，由于现在收集器 基本都采用分代收集算法，所以Java堆还可以细分为：新生代和老年代；新生代又可以分为：Eden 空间、From Survivor空间、To Survivor空间。</div><div>java堆是计算机物理存储上不连续的，逻辑上是连续的，也是大小可调节的(通过-Xms和-Xmx控制)</div><div>方法结束后，堆中的对象不会马上移出，仅仅在垃圾回收的时候才移除</div><div>如果在对中没有内存完成实力的分配，并且堆也无法再扩展时将会抛出OutOfMemoryError异常</div><div><br/></div><div><span style="font-size: unset;"><br/></span></div><div><br/></div><div>java7中 Hotspot虚拟机中java堆内存分为3个部分</div><ol><li><div>青年代</div></li><li><div>老年代</div></li><li><div>永久代(jdk8及其以后取消)</div></li></ol><div><br/></div><div>青年代：年轻代主要存放新创建的对象，内存大小相对会比较小，垃圾回收会比较频繁。年轻代分 成1个Eden Space和2个Suvivor Space（from 和to）。</div><div>老年代： 年老代主要存放JVM认为生命周期比较长的对象（经过几次的Young Gen的垃圾回收后仍 然存在），内存大小相对会比较大，垃圾回收也相对没有那么频繁。</div><div>堆空间大小= 青年代大小+老年代大小</div><div><br/></div></div><div>当创建对象时，会将对象放到青年代的伊甸园区(Eden) ,创建完之后 大多数的生命周期很短 用完之后 90%对象会被垃圾回收器销毁 剩余没有被销毁的会转到Suvivor 0 区 此时垃圾回收器会再次清理 如果在限定次数清理依旧没有清理干净 剩下的垃圾会进入到老年代 那此时清理频率就没有那么高了</div><div><br/></div><div><br/></div><div>对象分配过程;</div><div>如出现下图所示：左边是给对象分配空间的过程，右边是给垃圾设置对应的区域</div><div><img src="jvm_files/Image [4].png" type="image/png" data-filename="Image.png"/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div>方法区逻辑上也是属于堆区的一部分</div><div><br/></div><div>在jdk8及其以后元空间是方法区的实现</div><div>在jdk8及以后 实际上我们操作方法区时就是操作对应的元空间(元空间替代了jdk8以前的永久代)</div><div><br/></div><div><br/></div><div><img src="jvm_files/Image [5].png" type="image/png" data-filename="Image.png"/></div><div><br/></div><div><br/></div><div>方法区设置：</div><div>在jdk8以前方法区的落地实现采用的是永久代，通过设置永久代的大小来改变方法区的大小</div><div>jdk8及以后方法区的落地实现采用采用的是元空间，通过设置元空间大小来改变方法区的大小</div><div>方法区可以看作是一块独立于Java堆的内存空间，它主要是用来存储所加载 的类信息的</div><div><br/></div><div><br/></div><div>常量池：存放编译期间生成的各种字面量与符号引用</div><div>运行时常量池：常量池在运行期间的一个表现形式</div><div><br/></div><div>建立过多线程导致的栈内存溢出，在不能减少线程数和更换64位虚拟机的情况下，只能通过减少最大堆和减少栈容量来换取更多的线程</div><div><br/></div><div>jdk1.8及其以后 常量池是存放在堆空间的</div><div><br/></div><div>常量池内存溢出问题</div><div>解决：jdk7及其之后：调整堆空间大小</div><div><br/></div><div>元空间在本地内存中</div><div>jdk7及以后现在类的元信息存储在元空间中，静态变量和常量池等并入堆中，相当于原来的永久代中的数据，被元空间和堆内存给瓜分了。</div><div><br/></div><div>构造方法:用来创建对象</div><div>类构造方法：用来收集静态变量和执行一些静态代码块</div><div><br/></div><div>类的执行过程之初始化：</div><div><span style="font-size: unset; color: unset; font-family: unset;">初始化阶段就是执行类构造器clinit()方法的过程(类加载阶段)</span></div><div>初始化阶段在执行的时候javac自动编译clinit()方法并且该方法只会执行静态块和静态变量的赋值操作顺序自上而下</div><div>类加载过程早于对象初始化过程</div><div>init()方法就是类在执行过程中的构造方法、</div><div>init()方法：在对象初始化阶段执行</div><div>注意：接口中不执行clinit()方法</div><div>静态代码块只在类加载的时候执行一次</div><div>静态块在如果在有多个线程的情况下会有线程阻塞问题</div><div><br/></div><div>clinit()方法与init()方法的区别</div><div>clinit()方法:在初始化的过程会自动生成该方法，去给静态变量赋初值和执行静态代码块</div><div>init()方法：相当在调用构造方法创建对象时，会有对应的init()方法来执行</div><div>clinit()方法的执行时期: 类初始化阶段(该方法只能被jvm调用, 专门承担类变量的初始化工作) ,只执行一次</div><div>init()方法的执行时期: 对象的初始化阶段,可执行多次</div><div><br/></div><div>系统类加载器的 父类加载器为扩展类加载器</div><div>扩展类加载器的 父类加载器为启动类加载器</div><div>用户自定义的类加载器和系统类加载器是一样</div><div>所以用户自定义的类加载器时也会使用系统类加载器</div><div>启动类加载器通过代码的方式是无法获取到的</div><div><br/></div><div>类加载器的双亲委派</div><div>双亲委派工作过程： ：如果一个类加载器收到类加载的请求，它首先不会自己去尝试加载这个类，而是把这个 请求委派给父类加载器完成。每个类加载器都是如此，只有当父加载器在自己的搜索范围内找不到指定的类时 （即 ClassNotFoundException ），子加载器才会尝试自己去加载。</div><div><br/></div><div>如果要自定义类加载器 要重写findclass()方法</div><div><br/></div><div>垃圾回收集中在方法区和堆区,堆区属于重点区</div><div><br/></div><div>垃圾回收的优点：</div><ol><li><div>不需要考虑内存管理</div></li><li><div>可以有效的防止内存泄漏，有效的利用可使用的内存</div></li><li><div>由于有垃圾回收机制，Java中的对象不再有&quot;作用域&quot;的概念，只有对象的引用才有&quot;作用域&quot;</div></li></ol><div>缺点： java开发人员不了解自动内存管理, 内存管理就像一个黑匣子,过度依赖就会降低我们解决内存溢出/内存泄漏等问题的能力。</div><div><br/></div><div>引用计数算法的作用：判断对象是否存活</div><div>注意：java中并没有使用引用计数算法。</div><div>引用计数算法</div><div>优点： 实现简单，执行效率高，很好的和程序交织。</div><div>缺点： 无法检测出循环引用。就是说一段程序的两个或者多个互相引用</div><div><br/></div><div><br/></div><div>finalize()方法是object里的一个垃圾回收的方法</div><div>finalize()方法最终判定对象是否存活,只会执行一次</div><div>如果对象不想死，可以在finalize()方法做一些引用，从而完成自救</div><div><br/></div><div><br/></div><div>对象大多创建在伊甸园区</div><div><br/></div><div><br/></div><div>GC性能指标</div><div>吞吐量：即CPU用于运行用户代码的时间与CPU总消耗时间的比值（吞吐量 = 运行用户时间/(运行用户代码时间 + 垃圾收集时间))。代码运行时间越长越好 吞吐量越高越好</div><div>暂停时间：暂停时间越短越好</div><div>收集频率：越低越好</div><div>内存占用：与收集频率有关</div><div><br/></div><div><br/></div><div>parallel scavenge收集器与parallel old收集器进行组合就是jdk8默认的垃圾回收方式</div><div>parallel scavenge收集器：吞入量优先的收集器   它主要做吞吐量的优化</div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div><div><br/></div></div><div><br/></div></span>
</div></body></html> 